#include "AudioBuffer.h"
#include "WaveFile.h"

WaveFile::~WaveFile() {
    if(m_hFile != INVALID_HANDLE_VALUE) {
        CloseHandle(m_hFile);
    }
}

WaveFile *WaveFile::create(const TCHAR *filename, unsigned int sampleRate, unsigned int numChannels, unsigned int bytesPerSample) throw() {
    WaveFile *file = new WaveFile(filename, true);
    if(file->m_hFile == INVALID_HANDLE_VALUE) {
        delete(file);
        return(0);
    }

    DWORD written;
    RiffHeader rh = {{'R', 'I', 'F', 'F'}, 0, {'W', 'A', 'V', 'E'}};
    WriteFile(file->m_hFile, &rh, sizeof(RiffHeader), &written, 0);
    FormatHeader fh = {{'f', 'm', 't', ' '}, 16, 1, numChannels, sampleRate, sampleRate * numChannels * bytesPerSample, numChannels * bytesPerSample, bytesPerSample * 8};
    WriteFile(file->m_hFile, &fh, sizeof(FormatHeader), &written, 0);
    DataHeader dh = {{'d', 'a', 't', 'a'}, 0};
    WriteFile(file->m_hFile, &dh, sizeof(DataHeader), &written, 0);

    file->m_sampleRate = sampleRate;
    file->m_numChannels = numChannels;
    file->m_bytesPerSample = bytesPerSample;
    file->m_numSamples = 0;

    return(file);
}

WaveFile *WaveFile::open(const TCHAR *filename) throw() {
    WaveFile *file = new WaveFile(filename, false);
    if(file->m_hFile == INVALID_HANDLE_VALUE) {
        delete(file);
        return(0);
    }

    // Read the wave file header.
    DWORD bytesRead;
    RiffHeader rh;
    ReadFile(file->m_hFile, &rh, sizeof(RiffHeader), &bytesRead, 0);
    if(bytesRead != sizeof(RiffHeader) || rh.riff[0] != 'R' || rh.riff[1] != 'I' || rh.riff[2] != 'F' || rh.riff[3] != 'F' || rh.wave[0] != 'W' || rh.wave[1] != 'A' || rh.wave[2] != 'V' || rh.wave[3] != 'E') {
        delete(file);
        return(0);
    }
    // Read the format header.
    FormatHeader fh;
    ReadFile(file->m_hFile, &fh, sizeof(FormatHeader), &bytesRead, 0);
    if(bytesRead != sizeof(FormatHeader) || fh.fmt[0] != 'f' || fh.fmt[1] != 'm' || fh.fmt[2] != 't' || fh.fmt[3] != ' ' || fh.format != 1 || fh.numChannels != 1 || (fh.sampleRate != 11025 && fh.sampleRate != 22050 && fh.sampleRate != 44100) || fh.bitsPerSample > 16) {
        delete(file);
        return(0);
    }
    // Read the data header.
    DataHeader dh;
    ReadFile(file->m_hFile, &dh, sizeof(DataHeader), &bytesRead, 0);
    if(bytesRead != sizeof(DataHeader) || dh.data[0] != 'd' || dh.data[1] != 'a' || dh.data[2] != 't' || dh.data[3] != 'a') {
        delete(file);
        return(0);
    }

    file->m_sampleRate = fh.sampleRate;
    file->m_numChannels = fh.numChannels;
    file->m_bytesPerSample = fh.bitsPerSample / 8;
    file->m_numSamples = dh.chunkSize / (fh.numChannels * (fh.bitsPerSample / 8));

    file->m_dataStart = SetFilePointer(file->m_hFile, 0, 0, FILE_CURRENT);

    return(file);
}

void WaveFile::unlink() throw() {
    if(m_hFile != INVALID_HANDLE_VALUE) {
        CloseHandle(m_hFile);
        m_hFile = INVALID_HANDLE_VALUE;
        DeleteFile(m_filename);
    }
    delete(this);
}

void WaveFile::write(const AudioBuffer *buffer) throw() {
    ASSERT(m_sampleRate == buffer->samplesPerSecond() && m_numChannels == buffer->numChannels() && m_bytesPerSample == buffer->bytesPerSample());

    DWORD written;

    WriteFile(m_hFile, buffer->buffer(), buffer->bufferSize(), &written, 0);
}

void WaveFile::finalize() throw() {
    DWORD written;
    SetFilePointer(m_hFile, 4, 0, FILE_BEGIN);
    DWORD size = GetFileSize(m_hFile, 0) - 8;
    WriteFile(m_hFile, &size, 4, &written, 0);
    SetFilePointer(m_hFile, 40, 0, FILE_BEGIN);
    size -= 36;
    WriteFile(m_hFile, &size, 4, &written, 0);

    m_numSamples = size / (m_numChannels * m_bytesPerSample);
}

void WaveFile::read(void *buffer, unsigned int bufferSize, unsigned long long startSample) throw() {
    SetFilePointer(m_hFile, m_dataStart + (startSample * m_bytesPerSample * m_numChannels), 0, FILE_BEGIN);

    DWORD read;
    ReadFile(m_hFile, buffer, bufferSize, &read, 0);
}

MusicTime WaveFile::length() const throw() {
    return(MusicTime(m_numSamples, m_sampleRate));
}

unsigned int WaveFile::samplesPerSecond() const throw() {
    return(m_sampleRate);
}

unsigned int WaveFile::numChannels() const throw() {
    return(m_numChannels);
}

unsigned int WaveFile::bytesPerSample() const throw() {
    return(m_bytesPerSample);
}

WaveFile::WaveFile(const TCHAR *filename, bool write) throw(): m_hFile(INVALID_HANDLE_VALUE) {
    _tcscpy(m_filename, filename);
    m_hFile = CreateFile(filename, write ? GENERIC_WRITE : GENERIC_READ, write ? FILE_SHARE_WRITE : FILE_SHARE_READ, 0, write ? CREATE_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
}
